package com.example.easydownloadback.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.easydownloadback.domin.File;
import com.example.easydownloadback.domin.FileAndType;
import com.example.easydownloadback.domin.FileAssess;
import com.example.easydownloadback.domin.ResponseResult;
import com.example.easydownloadback.exception.ServiceException;
import com.example.easydownloadback.mapper.FileAndTypeMapper;
import com.example.easydownloadback.mapper.FileAssessMapper;
import com.example.easydownloadback.mapper.FileMapper;
import com.example.easydownloadback.service.FileAndTypeService;
import com.example.easydownloadback.service.FileAssessService;
import com.example.easydownloadback.service.FileService;
import com.example.easydownloadback.service.FileTypeService;
import com.example.easydownloadback.util.Code;
import com.example.easydownloadback.util.RedisIdWorker;
import com.example.easydownloadback.util.RedisPrefix;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.Collections;
import java.util.List;

@Service
public class FileServiceImpl extends ServiceImpl<FileMapper, File> implements FileService {
    @Value("${files.upload.path}")
    private String fileUploadPath;
    @Value("${files.upload.url}")
    private String fileUploadUrl;
    @Value("${files.upload.pictureUrl}")
    private String fileUploadPictureUrl;

    @Autowired
    private RedisIdWorker redisIdWorker;

    @Autowired
    private FileTypeService fileTypeService;

    @Autowired
    private FileAndTypeMapper fileAndTypeMapper;

    @Autowired
    private FileMapper fileMapper;

    @Autowired
    private FileAndTypeService fileAndTypeService;

    @Autowired
    private FileAssessService fileAssessService;

    @Autowired
    private FileAssessMapper fileAssessMapper;

    @Override
    public String uploadPicture(MultipartFile file) {
        //获取文件全名
        String originalFilename = file.getOriginalFilename();
        //获取文件类型
        String type = FileUtil.extName(originalFilename);

        //定义文件唯一标识码
        String id = redisIdWorker.nextId(RedisPrefix.FILE_URL_KEY);
        // uuid + . + 类型 生成新名称
        String uuFile = id + StrUtil.DOT + type;

        //将文件对象创建
        java.io.File uploadFile = new java.io.File(fileUploadPath + uuFile);

        //判断上传目录是否存在，不存在则创建目录
        //通过文件对象获取父目录是否存在判断
        if(!uploadFile.getParentFile().exists()) {
            uploadFile.getParentFile().mkdir();
        }
        //把获取到的文件存储到磁盘目录去
        try {
            file.transferTo(uploadFile);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //生成url
        return fileUploadPictureUrl + uuFile;
    }

    @Override
    public String upload(MultipartFile file) {
        //获取文件全名
        String originalFilename = file.getOriginalFilename();
        //将文件对象创建
        java.io.File uploadFile = new java.io.File(fileUploadPath + originalFilename);
        //判断上传目录是否存在，不存在则创建目录
        //通过文件对象获取父目录是否存在判断
        if(!uploadFile.getParentFile().exists()) {
            uploadFile.getParentFile().mkdir();
        }
        // 如果文件存在，直接返回url
        if (uploadFile.exists()) {
            return fileUploadUrl + originalFilename;
        }
        //把获取到的文件存储到磁盘目录去
        try {
            file.transferTo(uploadFile);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return fileUploadUrl + originalFilename;
    }

    @Override
    public void downLoad(String uuFile, HttpServletResponse response) {
        //创建文件对象 路径+文件名
        java.io.File downLoadFile = new java.io.File(fileUploadPath + uuFile);
        ServletOutputStream outputStream = null;
        try {
            //设置响应头
            response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(uuFile, "UTF-8"));
            //设置响应类型
            response.setContentType("application/octet-stream");
            //获取响应输出流
            outputStream = response.getOutputStream();
            //输出
            outputStream.write(FileUtil.readBytes(downLoadFile));
            //将缓存发送出去
            outputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭输出流
            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    @Transactional
    public ResponseResult<String> addFile(File file) {
        String fileId = redisIdWorker.nextId(RedisPrefix.FILE_ID_KEY);
        file.setId(fileId);
        save(file);
        // 添加评价表信息
        FileAssess fileAssess = new FileAssess();
        fileAssess.setId(fileId);
        fileAssess.setDownloadNum(0L);
        fileAssessService.addFileAssess(fileAssess);
        List<String> types = file.getTypes();
        if (types != null && types.size() > 0) {
            for (String type : types) {
                String tid = fileTypeService.getIdByTypeName(type);
                FileAndType fileAndType = new FileAndType();
                fileAndType.setId(redisIdWorker.nextId(RedisPrefix.FILE_AND_TYPE_ID_KEY));
                fileAndType.setFid(fileId);
                fileAndType.setTid(tid);
                fileAndTypeMapper.insert(fileAndType);
            }
        }
        return new ResponseResult<>(Code.success, "添加软件成功！");
    }

    @Override
    public ResponseResult<IPage<File>> listPage(Integer pageNum, Integer pageSize) {
        IPage<File> page = new Page<>(pageNum, pageSize);
        IPage<File> resultPage = query().orderByDesc("`id`").page(page);
        return new ResponseResult<IPage<File>>(Code.success, resultPage);
    }

    @Override
    public ResponseResult<IPage<File>> listPageBySelectAndType(Integer pageNum, Integer pageSize, String selectValue, String partition) {
        IPage<File> page = new Page<>(pageNum, pageSize);
        IPage<File> resultPage = null;
        if (StrUtil.isBlank(partition)) {
            resultPage = fileMapper.listPageBySelectAndTypeId(page, selectValue, null);
            return new ResponseResult<IPage<File>>(Code.success, resultPage);
        }
        String tid = fileTypeService.getIdByTypeName(partition);
        resultPage = fileMapper.listPageBySelectAndTypeId(page, selectValue, tid);
        return new ResponseResult<IPage<File>>(Code.success, resultPage);
    }

    @Override
    @Transactional
    public ResponseResult<String> deleteFileById(String fid) {
        removeById(fid);    // 级联删除了文件和类型关联信息
        return new ResponseResult<>(Code.success, "删除成功！");
    }

    @Override
    public ResponseResult<File> getFileById(String id) {
        File file = query().eq("id", id).one();
        return new ResponseResult<>(Code.success, file);
    }

    @Override
    @Transactional
    public ResponseResult updateFile(File file) {
        // 更新基础数据
        update().eq("id", file.getId()).update(file);
        // 删除原来的所有跟该软件相关联的“软件与类型表”相关数据
        fileAndTypeService.deleteByFileId(file.getId());
        // 获取类型列表
        List<String> types = file.getTypes();
        if (types != null && types.size() > 0) {
            for (String type : types) {
                String tid = fileTypeService.getIdByTypeName(type);
                FileAndType fileAndType = new FileAndType();
                fileAndType.setId(redisIdWorker.nextId(RedisPrefix.FILE_AND_TYPE_ID_KEY));
                fileAndType.setFid(file.getId());
                fileAndType.setTid(tid);
                fileAndTypeMapper.insert(fileAndType);
            }
        }
        return new ResponseResult<>(Code.success, "修改软件信息成功！");
    }

    @Override
    @Transactional
    public ResponseResult deleteFileBatch(String[] ids) {
        for (String fid : ids) {
            fileAndTypeService.deleteByFileId(fid);
            removeById(fid);
        }
        return new ResponseResult<>(Code.success, "批量删除成功！");
    }

    @Override
    public ResponseResult<IPage<File>> listFileByFileType(Integer pageNum, Integer pageSize, String fileType) {
        IPage<File> page = new Page<>(pageNum, pageSize);
        IPage<File> resultPage = null;
        if (StrUtil.isBlank(fileType)) {
            // 依据点击量排序
            resultPage = fileMapper.listPageByTypeIdSort(page, null);
        } else {
            String typeId = fileTypeService.getIdByTypeName(fileType);
            if (typeId == null) {
                // 没有的分类抛出异常
                throw new ServiceException(Code.service_error, "未知的文件分类！");
            } else {
                // 依据点击量排序，并且依据类型id查找
                resultPage = fileMapper.listPageByTypeIdSort(page, typeId);
            }
        }
        return new ResponseResult<>(Code.success, resultPage);
    }

    @Override
    public ResponseResult<IPage<File>> listFileBySelectData(Integer pageNum, Integer pageSize, String selectData) {
        IPage<File> page = new Page<>(pageNum, pageSize);
        IPage<File> resultPage = null;
        if (StrUtil.isBlank(selectData)) {
            // 依据点击量排序
            resultPage = fileMapper.listPageByNickNameSort(page, null);
        } else {
            // 依据点击量排序，并且依据nickname查找
            resultPage = fileMapper.listPageByNickNameSort(page, selectData);
        }
        // 查找每个文件信息的类型列表
        List<File> records = resultPage.getRecords();
        for (File record : records) {
            List<String> typeList = fileTypeService.getFileTypeListByFileId(record.getId()).getData();
            record.setTypes(typeList);
        }
        return new ResponseResult<>(Code.success, resultPage);
    }

    @Override
    public void downLoadPicture(String uuFile, HttpServletResponse response) {
        this.downLoad(uuFile, response);
    }

    @Override
    public void downLoadFile(String fileName, HttpServletResponse response) {
        this.downLoad(fileName, response);
        String fileUrl = fileUploadUrl + fileName;
        String fileId = getFileIdByFileUrl(fileUrl);
        if (fileId != null) {
            fileAssessMapper.addDownloadNumByFileId(fileId, 1);
        }
    }

    @Override
    public String getFileIdByFileUrl(String fileUrl) {
        File one = query().select("id").eq("file_url", fileUrl).one();
        return one == null ? null : one.getId();
    }
}
